﻿using Bit.Art.Core.Extensions;
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
using System.Linq;

namespace Bit.Art.Core.Models
{
    /// <summary>
    /// 交易订单
    /// </summary>
    [Serializable]
    public class Order : ISerializable, IComparable, IComparable<Order>, IFixByteSerializable
    {
        public Order() { }

        public Order(Guid id, uint uid, uint side, string market, decimal? price, decimal volume, decimal volumeRemaining, uint orderType, uint viplevel, DateTime placed, bool taker = false)
        {
            Id = id;
            UserId = uid;
            Side = side;
            Market = market;
            Price = price;
            Volume = volume;
            VolumeRemaining = volumeRemaining;
            OrderType = orderType;
            VipLevel = viplevel;
            PlacedDate = placed;
            Taker = taker;
        }

        /// <summary>
        /// 订单Id，在撮合成功
        /// </summary>
        public Guid Id { get; set; }

        /// <summary>
        /// 用户账户的Id
        /// </summary>
        public uint UserId { get; set; }

        /// <summary>
        /// 买卖方向，具体值参考<seealso cref="TradeSide"/>，默认值是买入。
        /// </summary>
        public uint Side { get; set; }

        /// <summary>
        /// 交易对，格式为：coin/quote，如以USDT购买BTC的交易对格式为：BTC/USDT。
        /// </summary>
        public string Market { get; set; }

        /// <summary>
        /// 下单价格，对于限价单<seealso cref="OrderType.Limit"/>，该值不能为空！
        /// </summary>
        public decimal? Price { get; set; }

        /// <summary>
        /// 下单数量
        /// </summary>
        public decimal Volume { get; set; }

        /// <summary>
        /// 剩余未成交的数量
        /// </summary>
        public decimal VolumeRemaining { get; set; }

        /// <summary>
        /// 本次撮合匹配的数量
        /// </summary>
        public decimal CurrentDealed { get; set; }

        /// <summary>
        /// 本次撮合的计价币数量
        /// </summary>
        public decimal CurrentDealedQuote { get; set; }

        /// <summary>
        /// 按计价币计算的已成交数量
        /// </summary>
        public decimal QuoteVolumeDealed { get; set; }

        /// <summary>
        /// 按计价币计算的下单数量
        /// </summary>
        public decimal QuoteVolume { get; set; }

        /// <summary>
        /// 本次订单锁仓的币的数量
        /// </summary>
        public decimal LockedBalance { get; set; }

        /// <summary>
        /// 订单类型，默认是<seealso cref="OrderType.Limit"/>
        /// </summary>
        public uint OrderType { get; set; }
        
        /// <summary>
        /// 下单日期
        /// </summary>
        public DateTime PlacedDate { get; set; }

        /// <summary>
        /// 是否被取消，默认是false
        /// </summary>
        public bool IsCancelled { get; set; }

        /// <summary>
        /// 是否是主动成交，默认是false，即maker
        /// </summary>
        public bool Taker { get; set; }

        /// <summary>
        /// 下单客户的VIP层级
        /// </summary>
        public uint VipLevel { get; set; } = 1000;

        /// <summary>
        /// 用在订单匹配引擎里的ZeroMq的PollerSocket的客户身份识别
        /// </summary>
        public byte[] ClientIdentity { get; set; }

        /// <summary>
        /// 序列化后的字节数组大小，值是184
        /// </summary>
        public int ByteSize => CUSTOM_SERIALIZE_SIZE;

        private const int CUSTOM_SERIALIZE_SIZE = 184;

        #region ISerializable成员
        public Order(SerializationInfo info, StreamingContext context)
        {
            IsCancelled = info.GetBoolean("c");
            PlacedDate = new DateTime(info.GetInt64("d"));
            CurrentDealed = (decimal)info.GetValue("cd", typeof(decimal));
            CurrentDealedQuote = (decimal)info.GetValue("cq", typeof(decimal));
            Id = (Guid)info.GetValue("i", typeof(Guid));
            Taker = (bool)info.GetValue("k", typeof(bool));
            VipLevel = info.GetUInt32("l");
            LockedBalance = (decimal)info.GetValue("lb", typeof(decimal));
            Market = (string)info.GetValue("m", typeof(string));
            Price = (decimal)info.GetValue("p", typeof(decimal));
            QuoteVolumeDealed = info.GetDecimal("q");
            QuoteVolume = info.GetDecimal("qv");
            VolumeRemaining = (decimal)info.GetValue("r", typeof(decimal));
            Side = (uint)info.GetValue("s", typeof(uint));
            OrderType = (uint)info.GetValue("t", typeof(uint));
            UserId = (uint)info.GetValue("u", typeof(uint));
            Volume = (decimal)info.GetValue("v", typeof(decimal));
        }

        public void GetObjectData(SerializationInfo info, StreamingContext context)
        {
            info.AddValue("c", IsCancelled);
            info.AddValue("cd", CurrentDealed);
            info.AddValue("cq", CurrentDealedQuote);
            info.AddValue("d", PlacedDate.ToUniversalTime().Ticks);
            info.AddValue("i", Id);
            info.AddValue("k", Taker);
            info.AddValue("l", VipLevel);
            info.AddValue("lb", LockedBalance);
            info.AddValue("m", Market);
            info.AddValue("p", Price.HasValue ? Price.Value : 0);
            info.AddValue("q", QuoteVolumeDealed);
            info.AddValue("qv", QuoteVolume);
            info.AddValue("r", VolumeRemaining);
            info.AddValue("s", Side);
            info.AddValue("t", OrderType);
            info.AddValue("u", UserId);
            info.AddValue("v", Volume);
        }
        #endregion

        /// <summary>
        /// 将一个订单序列化成字节数组，采用自定义的序列化以节省空间
        /// </summary>
        /// <param name="item">要序列化的订单</param>
        /// <returns>序列化后的字节</returns>
        /// <remarks>
        /// 格式：
        /// 000 - 015: Id
        /// 016 - 019: UserId
        /// 020 - 031: Market，即交易对不能超过12个字符
        /// 032 - 047: Price, decimal类型需要4个大小的int数组，也就是16个字节，如果值为负数，则表示为null
        /// 048 - 063: Volume
        /// 064 - 079: VolumeRemaining
        /// 080 - 095: QuoteVolumeDealed,
        /// 096 - 111: QuoteVolume
        /// 112 - 127: LockedBalance
        /// 128 - 143: CurrentDealed
        /// 144 - 159: CurrentDealedQuote
        /// 160 - 167: PlacedDate
        /// 168 - 170: VipLevel
        /// 171 - 179: ClientIdentity
        /// 180:       Side
        /// 181:       OrderType,
        /// 182:       IsCancelled
        /// 183:       Taker
        /// </remarks>
        public byte[] ToFixedBytes()
        {
            var bytes = new byte[CUSTOM_SERIALIZE_SIZE];
            // Id
            var serializedBytes = Id.ToByteArray();
            Array.Copy(serializedBytes, bytes, 16);

            // UserId
            serializedBytes = BitConverter.GetBytes(UserId);
            Array.Copy(serializedBytes, 0, bytes, 16, 4);

            // Market
            if (!string.IsNullOrWhiteSpace(Market))
            {
                serializedBytes = System.Text.Encoding.ASCII.GetBytes(Market);
                Array.Copy(serializedBytes, 0, bytes, 20, Math.Min(12, serializedBytes.Length));
            }

            // Price
            Utility.Serialize(Price.HasValue ? Price.Value : -1, bytes, 32);
            // Volume
            Utility.Serialize(Volume, bytes, 48);
            // VolumeRemaining
            Utility.Serialize(VolumeRemaining, bytes, 64);
            // QuoteVolumeDealed
            Utility.Serialize(QuoteVolumeDealed, bytes, 80);
            // QuoteVolume
            Utility.Serialize(QuoteVolume, bytes, 96);
            // LockedBalance
            Utility.Serialize(LockedBalance, bytes, 112);
            // CurrentDealed
            Utility.Serialize(CurrentDealed, bytes, 128);
            // CurrentDealedQuote
            Utility.Serialize(CurrentDealedQuote, bytes, 144);
            // PlacedDate
            serializedBytes = BitConverter.GetBytes(PlacedDate.Ticks);
            Array.Copy(serializedBytes, 0, bytes, 160, 8);
            // VipLevel
            serializedBytes = BitConverter.GetBytes(VipLevel);
            Array.Copy(serializedBytes, 0, bytes, 168, 3);
            // ClientIdentity
            if (ClientIdentity != null)
                Array.Copy(ClientIdentity, 0, bytes, 171, ClientIdentity.Length);

            // Side
            bytes[180] = (byte)Side;
            // OrderType
            bytes[181] = (byte)OrderType;
            // IsCancelled
            bytes[182] = IsCancelled ? (byte)1 : (byte)0;
            // Taker
            bytes[183] = Taker ? (byte)1 : (byte)0;

            return bytes;
        }

        public static Order FromBytes(byte[] bytes)
        {
            var item = new Order();
            item.InitFrom(bytes);
            return item;
        }
        
        private const decimal FEERATEBASE = 1000000m;
        private decimal? _feerate;
        /// <summary>
        /// 获取这个订单应该收取的手续费率
        /// </summary>
        /// <returns>小数点形式的费率，默认值是0.001。</returns>
        public decimal GetFeeRate()
        {
            if (!_feerate.HasValue)
                _feerate = VipLevel / FEERATEBASE;

            return _feerate.Value;
        }

        /// <summary>
        /// 用于disruptor的ringbuffer的预先数据内存分配，避免经常GC，或者有意外的引用导致GC无法释放。
        /// </summary>
        /// <param name="other">从消息队列里反序列化而来的订单信息</param>
        /// <remarks>
        /// <paramref name="other"/>应该是只用来在消息队列反序列化操作，不要在Update函数里保存指向other的引用，否则会导致other无法GC导致内存泄露！
        /// </remarks>
        public void Update(Order other)
        {
            ClientIdentity = other.ClientIdentity;
            CurrentDealed = other.CurrentDealed;
            CurrentDealedQuote = other.CurrentDealedQuote;
            Id = other.Id;
            IsCancelled = other.IsCancelled;
            LockedBalance = other.LockedBalance;
            Market = other.Market;
            OrderType = other.OrderType;
            PlacedDate = other.PlacedDate;
            Price = other.Price;
            QuoteVolume = other.QuoteVolume;
            QuoteVolumeDealed = other.QuoteVolumeDealed;
            Side = other.Side;
            Taker = other.Taker;
            UserId = other.UserId;
            VipLevel = other.VipLevel;
            Volume = other.Volume;
            VolumeRemaining = other.VolumeRemaining;
        }

        public int CompareTo(object obj)
        {
            if (obj == null)
            {
                return 1;
            }
            else if (obj is Guid)
            {
                var other = (Guid)obj;
                return Id.CompareTo(other);
            }
            else if (obj is Order)
            {
                return CompareTo((Order)obj);
            }
            else
            {
                throw new NotSupportedException(string.Format("不支持将Order与类型{0}对比！", obj.GetType()));
            }
        }

        public int CompareTo(Order other)
        {
            if (other == null)
            {
                return 1;
            }
            else
            {
                return Id.CompareTo(other.Id);
            }
        }

        public void InitFrom(byte[] bytes)
        {
            // Id
            var serializedBytes = new byte[16];
            Array.Copy(bytes, serializedBytes, serializedBytes.Length);
            Id = new Guid(serializedBytes);

            // UserId
            serializedBytes = new byte[4];
            Array.Copy(bytes, 16, serializedBytes, 0, serializedBytes.Length);
            UserId = BitConverter.ToUInt32(serializedBytes, 0);

            // Market
            var marketLen = 0;
            for (var i = 20; i < 32; ++i, ++marketLen)
            {
                if (bytes[i] == 0)
                    break;
            }
            serializedBytes = new byte[marketLen];
            Array.Copy(bytes, 20, serializedBytes, 0, marketLen);
            Market = Encoding.ASCII.GetString(serializedBytes);

            // Price
            Price = Utility.DecimalFromBytes(bytes, 32);
            Price = Price < 0 ? null : Price;

            // Volume
            Volume = Utility.DecimalFromBytes(bytes, 48);
            // VolumeRemaining
            VolumeRemaining = Utility.DecimalFromBytes(bytes, 64);
            // QuoteVolumeDealed
            QuoteVolumeDealed = Utility.DecimalFromBytes(bytes, 80);
            // QuoteVolume
            QuoteVolume = Utility.DecimalFromBytes(bytes, 96);
            // LockedBalance
            LockedBalance = Utility.DecimalFromBytes(bytes, 112);
            // CurrentDealed
            CurrentDealed = Utility.DecimalFromBytes(bytes, 128);
            // CurrentDealedQuote
            CurrentDealedQuote = Utility.DecimalFromBytes(bytes, 144);
            // PlacedDate
            serializedBytes = new byte[8];
            Array.Copy(bytes, 160, serializedBytes, 0, serializedBytes.Length);
            PlacedDate = new DateTime(BitConverter.ToInt64(serializedBytes, 0));
            // VipLevel
            serializedBytes = new byte[4];
            Array.Copy(bytes, 168, serializedBytes, 0, 3);
            VipLevel = BitConverter.ToUInt32(serializedBytes, 0);
            // ClientIdentity
            // TODO: ClientIdentity字段在ZeroMQ里DealerSocket分配的是5个字节大小的数组
            // 我这里为了字节的对齐改成了9个字节的数组，不知道会不会影响ZeroMQ
            ClientIdentity = new byte[9];
            Array.Copy(bytes, 171, ClientIdentity, 0, 9);

            // Side
            Side = bytes[180];
            // OrderType
            OrderType = bytes[181];
            // IsCancelled
            IsCancelled = bytes[182] == 1;
            // Taker
            Taker = bytes[183] == 1;
        }
    }
}
